package ms.jacrim.framework;

import java.util.ArrayList;
import java.util.List;

import javax.xml.xpath.XPathExpressionException;

import ms.jacrim.pddl.PddlProblem;
import ms.jacrim.pddl.PddlProblemGenerator;
import ms.utils.XEOList;
import ms.utils.XmlFile;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class PddlModel {
	private XmlFile source;
	private Element pddlRoot;

	private ArrayList<String> customFact;
	private XEOList<Goal> goals;
	private XEOList<Actor> actors;

	private List<String> postCommands;

	static class PddlElement extends ms.utils.XmlElementObject {
		public PddlElement(Element node) {
			super(node);
		}

		public String Id() {
			return getAttribute("id");
		}

		public void setId(String value) {
			setAttribute("id", value);
		}

		protected Element getNode(String tagName, boolean allowCreate) {
			NodeList requests = Node.getElementsByTagName(tagName);
			return (Element) (requests.getLength() > 0 ? requests.item(0) : allowCreate ? this.addNode("request") : null);
		}
	}

	public static class Goal extends PddlElement {

		public Goal(Element node) {
			super(node);
		}

		public boolean isTopGoal() {
			return "yes".equalsIgnoreCase(getAttribute("goal"));
		}
	}

	public static class Actor extends PddlElement {

		public Actor(Element node) {
			super(node);
		}

		private Element getRequestNode() {
			return getNode("request", true);
		}

		public String[] getRequestGoals() {
			Element rqNode = getRequestNode();
			if (rqNode != null)
				return rqNode.getAttribute("goals").split(",");
			return new String[] {};
		}

		public void addRequest(String... goal) {
			Element rqNode = getRequestNode();
			if (rqNode != null) {
				String request = ',' + rqNode.getAttribute("goals") + ',';
				for (String g : goal) {
					int idx = request.indexOf(',' + g + ',');
					if (idx < 0)
						request += g + ',';
				}
				rqNode.setAttribute("goals", request.substring(1, request.length() - 1));
			}
		}
		
		public void removeRequest(String... goal) {
			Element rqNode = getRequestNode();
			if (rqNode != null) {
				String request = ',' + rqNode.getAttribute("goals") + ',';
				for (String g : goal) {
					request = request.replace(',' + g + ',', ",");
				}
				rqNode.setAttribute("goals", request.substring(1, request.length() - 1));
			}
		}
	}

	public PddlModel(XmlFile source) throws Exception {
		this.source = source;
		pddlRoot = source.selectSingleNode("pddl");

		Element goalsPart = source.selectSingleNode("fact/goals", pddlRoot);
		Element actorsPart = source.selectSingleNode("fact/actors", pddlRoot);

		goals = new XEOList<Goal>(source, goalsPart, "goal", Goal.class);
		actors = new XEOList<Actor>(source, actorsPart, "actor", Actor.class);

		postCommands = new ArrayList<String>();
		
		parseCustomPart();
	}

	private void parseCustomPart() throws XPathExpressionException {
		Element customPartElement = source.selectSingleNode("fact/custom", pddlRoot);
		customFact = new ArrayList<String>();

		if (customPartElement != null) {
			String customPart = customPartElement.getTextContent().replaceAll("\r", "");
			for (String s : customPart.split("\n")) {
				String line = s.trim().replace("\t", "");
				if (line.length() > 0 && !line.startsWith(";")) {
					customFact.add(line);
				}
			}
		}
	}

	public XEOList<Goal> Goals() {
		return goals;
	}

	public XEOList<Actor> Actors() {
		return actors;
	}

	public void introduceCustomFact(String fact) {
		customFact.add(fact);
	}

	public void removeCustomFact(String fact) {
		customFact.remove(fact);
	}

	public synchronized void addPostCommand(String cmd) {
		postCommands.add(cmd);
	}
	
	public synchronized void removePostCommand(String cmd) {
		postCommands.remove(cmd);
	}
	
	private PddlProblem processPostCommand(PddlProblem pddl) {
		for(String cmd: postCommands) {
			String arg = cmd.substring(2);
			if (cmd.startsWith("+!")) 
				pddl.addGoalPredicate(arg);
			else if (cmd.startsWith("-!"))
				pddl.removeGoalPredicate(arg);
			else if (cmd.startsWith("+@"))
				pddl.addFact(arg);
			else if (cmd.startsWith("-@"))
				pddl.removeFact(arg);
		}
		return pddl;
	}
	
	public PddlProblem generatePddl() throws Exception {
		PddlProblemGenerator ppg = new PddlProblemGenerator();
		ppg.setConfig(source);
		return processPostCommand(ppg.generate());
	}
}
